package com.jianjun.framework.web.interceptor;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.StrUtil;
import com.jfinal.aop.Inject;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.Kv;
import com.jfinal.kit.ThreadPoolKit;
import com.jianjun.common.constant.Constants;
import com.jianjun.common.constant.HttpStatus;
import com.jianjun.common.constant.UserConstants;
import com.jianjun.common.util.SecurityUtils;
import com.jianjun.framework.manager.factory.AsyncFactory;
import com.jianjun.framework.security.LoginUser;
import com.jianjun.framework.security.service.PermissionService;
import com.jianjun.framework.web.domain.AjaxResult;
import com.jianjun.project.common.model.SysRole;
import com.jianjun.project.common.model.SysUser;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 *
 * @author source
 * {@code @date} 2025/1/8 14:46
 */
@Slf4j
public class DataScopeInterceptor implements Interceptor {
    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";
    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";
    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";
    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";
    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";
    static ThreadLocal<Kv> TL_DATA_SCOPE = new ThreadLocal<>();
    @Inject
    PermissionService permissionService;

    public static Kv getDataScope() {
        return TL_DATA_SCOPE.get();
    }

    @Override
    public void intercept(Invocation inv) {
        TimeInterval timer = DateUtil.timer();
        TL_DATA_SCOPE.set(Kv.create());
        try {
            DataScope dataScope = getAnnotation(inv);
            if (Objects.nonNull(dataScope)) {
                String permission = dataScope.permission();
                if (StrUtil.isNotBlank(permission) && !permissionService.hasPermi(permission)) {
                    log.error("请求地址'{}',权限校验失败'{}'", inv.getController().getRequest().getRequestURI(), "不允许访问");
                    inv.getController().renderJson(JsonKit.toJson(AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限，请联系管理员授权")));
                    ThreadPoolKit.execute(AsyncFactory.recordOper(inv, timer, new Exception("没有权限，请联系管理员授权")));
                    return;
                }
                handleDataScope(dataScope);
            }
            inv.invoke();
            ThreadPoolKit.execute(AsyncFactory.recordOper(inv, timer, null));
        } catch (RuntimeException e) {
            log.error("请求地址'{}',发生未知异常.", inv.getController().getRequest().getRequestURI(), e);
            inv.getController().renderJson(JsonKit.toJson(AjaxResult.error(e.getMessage())));
            ThreadPoolKit.execute(AsyncFactory.recordOper(inv, timer, e));
        } catch (Exception e) {
            log.error("请求地址'{}',发生系统异常.", inv.getController().getRequest().getRequestURI(), e);
            inv.getController().renderJson(JsonKit.toJson(AjaxResult.error(e.getMessage())));
            ThreadPoolKit.execute(AsyncFactory.recordOper(inv, timer, e));
        }
    }

    private DataScope getAnnotation(Invocation inv) {
        DataScope dataScope = inv.getController().getClass().getAnnotation(DataScope.class);
        return dataScope != null ? dataScope : inv.getMethod().getAnnotation(DataScope.class);
    }

    private void handleDataScope(DataScope dataScope) {
        // 获取当前的用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (Objects.nonNull(loginUser) && Objects.nonNull(dataScope)) {
            SysUser currentUser = loginUser.getUser();
            // 如果是超级管理员，则不过滤数据
            if (Objects.nonNull(currentUser) && !currentUser.isAdmin()) {
                dataScopeFilter(currentUser, dataScope.deptAlias(), dataScope.userAlias());
            }
        }
    }

    /**
     * 数据范围过滤
     *
     * @param user 用户
     * @param deptAlias 部门别名
     * @param userAlias 用户别名
     */
    private void dataScopeFilter(SysUser user, String deptAlias, String userAlias) {
        StringBuilder sqlString = new StringBuilder();
        List<String> conditions = new ArrayList<>();
        List<String> scopeCustomIds = new ArrayList<>();
        user.getRoles().forEach(role -> {
            if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && UserConstants.ROLE_NORMAL.equals(role.getStatus())) {
                scopeCustomIds.add(Convert.toStr(role.getRoleId()));
            }
        });

        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
            if (conditions.contains(dataScope) || StrUtil.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) {
                continue;
            }
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                sqlString = new StringBuilder();
                conditions.add(dataScope);
                break;
            } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
                if (scopeCustomIds.size() > 1) {
                    // 多个自定数据权限使用in查询，避免多次拼接。
                    sqlString.append(StrUtil.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(Constants.DELIMETER, scopeCustomIds)));
                } else {
                    sqlString.append(StrUtil.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
                }
            } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
                sqlString.append(StrUtil.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
            } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                sqlString.append(StrUtil.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
            } else if (DATA_SCOPE_SELF.equals(dataScope)) {
                if (StrUtil.isNotBlank(userAlias)) {
                    sqlString.append(StrUtil.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
                } else {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    sqlString.append(StrUtil.format(" OR {}.dept_id = 0 ", deptAlias));
                }
            }
            conditions.add(dataScope);
        }

        // 角色都不包含传递过来的权限字符，这个时候sqlString也会为空，所以要限制一下,不查询任何数据
        if (CollectionUtil.isEmpty(conditions)) {
            sqlString.append(StrUtil.format(" OR {}.dept_id = 0 ", deptAlias));
        }

        if (StrUtil.isNotBlank(deptAlias) && StrUtil.isNotBlank(sqlString.toString())) {
            System.err.println(sqlString);
            TL_DATA_SCOPE.get().set(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
        }
    }
}

